package com.sun.showcase.conf;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import com.sun.showcase.conf.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.web.cors.CorsUtils;


@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private DataSource dataSource;

	
	@Bean
	@Override
	protected AuthenticationManager authenticationManager() throws Exception {

		return super.authenticationManager();
	}

	
	@Override
	protected void configure(HttpSecurity http) throws Exception {

			// 禁用csrf防御
		http.csrf().disable()
			.addFilterBefore(new TenantCodeFilter(), UsernamePasswordAuthenticationFilter.class)
			// 权限控制
			.addFilterAt(customerFilterSecurityInterceptor(),FilterSecurityInterceptor.class)
			// 允许所有用户访问
			.authorizeRequests()
			.antMatchers("/file/**","/login","/login/**", "/timeout", "/multiple", "/static/**","/UEditor","/file/download","**/*.js","**/*.css")
			.permitAll()
			.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
			// 其它访问地址均需验证权限(已登录)
			.anyRequest()
			.authenticated()
			.and()
			.formLogin()
			.successHandler(loginSuccessHandler())
			// 指定登录页面
			.loginPage("/login")
			//.usernameParameter("name")
			// 默认成功跳转页面
			.defaultSuccessUrl("/home", true)
			//.successForwardUrl("/home")
			// 登录成功后存储用户信息
			.failureUrl("/login?error=true").permitAll()
			.and()
			.logout()
			// 退出登录后默认地址
			.logoutSuccessUrl("/login")
			// HttpSession无效
			//.invalidateHttpSession(true)
			.permitAll()
			.and()
			// 允许iframe加载同源的资源
			.headers()
			.frameOptions()
			.sameOrigin()
			.and()
			// 登录后记住用户,下次自动登录
			//.rememberMe()
			//.tokenValiditySeconds(1209600)
			// 指定记住登录信息所有使用的数据源
			//.tokenRepository(tokenRepository())
			//.and()
			// 并发session控制
			.sessionManagement()
			// 会话超时跳转页面
			.invalidSessionUrl("/timeout")
			.maximumSessions(1)
			// 并发登陆
			.expiredSessionStrategy(new ResponseBodySessionInformationExpiredStrategy());
	}

	
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth)throws Exception {

		auth.authenticationProvider(authenticationProvider());

		// 不删除凭证,以便记住用户
		auth.eraseCredentials(false);
	}

	
	// 权限拦截器
	@Bean
	public CustomerFilterSecurityInterceptor customerFilterSecurityInterceptor() {

		CustomerFilterSecurityInterceptor interceptor = new CustomerFilterSecurityInterceptor();

		interceptor.setAccessDecisionManager(new SecurityAccessDecisionManager());

		interceptor.setSecurityMetadataSource(new InvocationSecurityMetadataSourceService());

		return interceptor;
	}

	
	// 指定密码加密所使用的加密器(passwordEncoder)
	// 需要将密码加密后写入数据库
	// 注册自定义认证
	@Bean
	public DaoAuthenticationProvider authenticationProvider() {

		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();

		provider.setUserDetailsService(userDetailsService());
		provider.setHideUserNotFoundExceptions(false);
		provider.setPasswordEncoder(passwordEncoder());

		return provider;
	}

	
	// 指定加密器
	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
	
	// 记住我
	@Bean
	public JdbcTokenRepositoryImpl tokenRepository() {

		JdbcTokenRepositoryImpl j = new JdbcTokenRepositoryImpl();
		j.setDataSource(dataSource);

		return j;
	}
	

	// 登陆成功处理业务
	@Bean
	public LoginSuccesshandler loginSuccessHandler() {
		return new LoginSuccesshandler();
	}

	
	// 登陆请求
	@Bean
	public UserDetailsService userDetailsService() {
		return new UserDetailsServiceImpl();
	}
	

	// 并发登陆
	private static final class ResponseBodySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {

		public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException,ServletException {

			HttpServletResponse response = event.getResponse();

			HttpServletRequest request = event.getRequest();

			if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {

				response.sendError(HttpServletResponse.SC_UNAUTHORIZED);

			} else {

				response.setContentType("text/html");

				response.setCharacterEncoding("utf-8");

				response.getWriter().print("登录超时,或该账号已在另一处登录,请重新登录...");

				response.flushBuffer();
			}
		}
	}
	
    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/assets/**","/layui/**","/js/**","/favicon.ico");
    }
}
